package com.datagateway.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 数据加密管理器
 * 负责敏感数据的加密存储和传输，确保数据安全
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class DataEncryptionManager {

    private static final Logger logger = LoggerFactory.getLogger(DataEncryptionManager.class);

    @Autowired
    private AlertManager alertManager;

    @Autowired
    private SystemMonitor systemMonitor;

    /**
     * 加密统计信息
     */
    private final AtomicLong totalEncryptions = new AtomicLong(0);
    private final AtomicLong totalDecryptions = new AtomicLong(0);
    private final AtomicLong totalEncryptionTime = new AtomicLong(0);
    private final AtomicLong totalDecryptionTime = new AtomicLong(0);

    /**
     * 加密配置
     */
    private boolean encryptionEnabled = true;
    private String defaultAlgorithm = "AES/GCM/NoPadding";
    private int keySize = 256;
    private int gcmIvLength = 12;
    private int gcmTagLength = 128;
    private String keyDerivationAlgorithm = "PBKDF2WithHmacSHA256";
    private int keyDerivationIterations = 10000;

    /**
     * 密钥管理
     */
    private final Map<String, SecretKey> encryptionKeys = new ConcurrentHashMap<>();
    private final Map<String, String> keyAliases = new ConcurrentHashMap<>();
    private String masterKeyAlias = "master";
    private String defaultKeyAlias = "default";

    /**
     * 敏感字段配置
     */
    private final Set<String> sensitiveFields = new HashSet<>();
    private final Map<String, EncryptionRule> fieldEncryptionRules = new ConcurrentHashMap<>();

    /**
     * 初始化加密管理器
     */
    public void initialize() {
        try {
            // 初始化默认密钥
            initializeDefaultKeys();
            
            // 初始化敏感字段配置
            initializeSensitiveFields();
            
            // 初始化字段加密规则
            initializeFieldEncryptionRules();
            
            logger.info("数据加密管理器初始化成功");
            
        } catch (Exception e) {
            logger.error("数据加密管理器初始化失败", e);
            alertManager.sendSystemErrorAlert("数据加密管理器初始化失败", e.getMessage());
        }
    }

    /**
     * 加密数据
     * 
     * @param data 要加密的数据
     * @param keyAlias 密钥别名
     * @return 加密后的数据
     */
    public EncryptedData encryptData(String data, String keyAlias) {
        if (!encryptionEnabled || data == null || data.isEmpty()) {
            return new EncryptedData(data, null, null, null, false);
        }

        long startTime = System.currentTimeMillis();
        
        try {
            SecretKey key = getEncryptionKey(keyAlias);
            if (key == null) {
                logger.warn("未找到加密密钥: {}", keyAlias);
                return new EncryptedData(data, null, null, null, false);
            }

            // 生成随机IV
            byte[] iv = generateRandomIV();
            
            // 创建加密器
            Cipher cipher = Cipher.getInstance(defaultAlgorithm);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(gcmTagLength, iv);
            cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec);
            
            // 执行加密
            byte[] encryptedData = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            
            // 更新统计信息
            totalEncryptions.incrementAndGet();
            totalEncryptionTime.addAndGet(System.currentTimeMillis() - startTime);
            
            logger.debug("数据加密成功: {} 字节 -> {} 字节", data.length(), encryptedData.length);
            
            return new EncryptedData(
                Base64.getEncoder().encodeToString(encryptedData),
                Base64.getEncoder().encodeToString(iv),
                keyAlias,
                defaultAlgorithm,
                true
            );
            
        } catch (Exception e) {
            logger.error("数据加密失败", e);
            alertManager.sendSystemErrorAlert("数据加密失败", e.getMessage());
            return new EncryptedData(data, null, null, null, false);
        }
    }

    /**
     * 解密数据
     * 
     * @param encryptedData 加密的数据
     * @return 解密后的数据
     */
    public String decryptData(EncryptedData encryptedData) {
        if (!encryptionEnabled || encryptedData == null || !encryptedData.isEncrypted()) {
            return encryptedData != null ? encryptedData.getData() : null;
        }

        long startTime = System.currentTimeMillis();
        
        try {
            SecretKey key = getEncryptionKey(encryptedData.getKeyAlias());
            if (key == null) {
                logger.warn("未找到解密密钥: {}", encryptedData.getKeyAlias());
                return encryptedData.getData();
            }

            // 解码数据
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData.getData());
            byte[] iv = Base64.getDecoder().decode(encryptedData.getIv());
            
            // 创建解密器
            Cipher cipher = Cipher.getInstance(encryptedData.getAlgorithm());
            GCMParameterSpec gcmSpec = new GCMParameterSpec(gcmTagLength, iv);
            cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec);
            
            // 执行解密
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8);
            
            // 更新统计信息
            totalDecryptions.incrementAndGet();
            totalDecryptionTime.addAndGet(System.currentTimeMillis() - startTime);
            
            logger.debug("数据解密成功: {} 字节 -> {} 字节", encryptedBytes.length, decryptedData.length());
            
            return decryptedData;
            
        } catch (Exception e) {
            logger.error("数据解密失败", e);
            alertManager.sendSystemErrorAlert("数据解密失败", e.getMessage());
            return encryptedData.getData();
        }
    }

    /**
     * 加密对象中的敏感字段
     * 
     * @param dataMap 数据对象
     * @return 加密后的数据对象
     */
    public Map<String, Object> encryptSensitiveFields(Map<String, Object> dataMap) {
        if (!encryptionEnabled || dataMap == null || dataMap.isEmpty()) {
            return dataMap;
        }

        Map<String, Object> encryptedMap = new HashMap<>(dataMap);
        
        try {
            for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
                String fieldName = entry.getKey();
                Object fieldValue = entry.getValue();
                
                if (isSensitiveField(fieldName) && fieldValue instanceof String) {
                    String stringValue = (String) fieldValue;
                    if (!stringValue.isEmpty()) {
                        EncryptionRule rule = getFieldEncryptionRule(fieldName);
                        String keyAlias = rule != null ? rule.getKeyAlias() : defaultKeyAlias;
                        
                        EncryptedData encrypted = encryptData(stringValue, keyAlias);
                        if (encrypted.isEncrypted()) {
                            encryptedMap.put(fieldName, encrypted);
                        }
                    }
                }
            }
            
            logger.debug("敏感字段加密完成: {} 个字段", encryptedMap.size());
            
        } catch (Exception e) {
            logger.error("敏感字段加密失败", e);
            alertManager.sendSystemErrorAlert("敏感字段加密失败", e.getMessage());
        }
        
        return encryptedMap;
    }

    /**
     * 解密对象中的敏感字段
     * 
     * @param dataMap 数据对象
     * @return 解密后的数据对象
     */
    public Map<String, Object> decryptSensitiveFields(Map<String, Object> dataMap) {
        if (!encryptionEnabled || dataMap == null || dataMap.isEmpty()) {
            return dataMap;
        }

        Map<String, Object> decryptedMap = new HashMap<>(dataMap);
        
        try {
            for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
                String fieldName = entry.getKey();
                Object fieldValue = entry.getValue();
                
                if (fieldValue instanceof EncryptedData) {
                    EncryptedData encrypted = (EncryptedData) fieldValue;
                    String decryptedValue = decryptData(encrypted);
                    decryptedMap.put(fieldName, decryptedValue);
                }
            }
            
            logger.debug("敏感字段解密完成: {} 个字段", decryptedMap.size());
            
        } catch (Exception e) {
            logger.error("敏感字段解密失败", e);
            alertManager.sendSystemErrorAlert("敏感字段解密失败", e.getMessage());
        }
        
        return decryptedMap;
    }

    /**
     * 生成新的加密密钥
     * 
     * @param keyAlias 密钥别名
     * @return 是否生成成功
     */
    public boolean generateNewKey(String keyAlias) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(keySize);
            SecretKey key = keyGenerator.generateKey();
            
            encryptionKeys.put(keyAlias, key);
            logger.info("新加密密钥已生成: {}", keyAlias);
            
            return true;
            
        } catch (Exception e) {
            logger.error("生成加密密钥失败: {}", keyAlias, e);
            alertManager.sendSystemErrorAlert("生成加密密钥失败", e.getMessage());
            return false;
        }
    }

    /**
     * 添加敏感字段
     * 
     * @param fieldName 字段名
     * @param encryptionRule 加密规则
     */
    public void addSensitiveField(String fieldName, EncryptionRule encryptionRule) {
        sensitiveFields.add(fieldName);
        if (encryptionRule != null) {
            fieldEncryptionRules.put(fieldName, encryptionRule);
        }
        
        logger.info("敏感字段已添加: {} -> {}", fieldName, encryptionRule);
    }

    /**
     * 移除敏感字段
     * 
     * @param fieldName 字段名
     */
    public void removeSensitiveField(String fieldName) {
        sensitiveFields.remove(fieldName);
        fieldEncryptionRules.remove(fieldName);
        
        logger.info("敏感字段已移除: {}", fieldName);
    }

    /**
     * 检查字段是否为敏感字段
     * 
     * @param fieldName 字段名
     * @return 是否为敏感字段
     */
    public boolean isSensitiveField(String fieldName) {
        return sensitiveFields.contains(fieldName);
    }

    /**
     * 获取字段加密规则
     * 
     * @param fieldName 字段名
     * @return 加密规则
     */
    public EncryptionRule getFieldEncryptionRule(String fieldName) {
        return fieldEncryptionRules.get(fieldName);
    }

    /**
     * 获取加密统计信息
     * 
     * @return 加密统计信息
     */
    public EncryptionStatistics getStatistics() {
        return new EncryptionStatistics(
            totalEncryptions.get(),
            totalDecryptions.get(),
            totalEncryptionTime.get(),
            totalDecryptionTime.get(),
            encryptionKeys.size(),
            sensitiveFields.size(),
            encryptionEnabled,
            defaultAlgorithm,
            keySize,
            System.currentTimeMillis()
        );
    }

    /**
     * 设置加密配置
     * 
     * @param enabled 是否启用加密
     * @param algorithm 加密算法
     * @param keySize 密钥长度
     * @param gcmIvLength GCM IV长度
     * @param gcmTagLength GCM标签长度
     */
    public void setEncryptionConfig(boolean enabled, String algorithm, int keySize, 
                                  int gcmIvLength, int gcmTagLength) {
        this.encryptionEnabled = enabled;
        this.defaultAlgorithm = algorithm;
        this.keySize = keySize;
        this.gcmIvLength = gcmIvLength;
        this.gcmTagLength = gcmTagLength;
        
        logger.info("加密配置已更新: 启用={}, 算法={}, 密钥长度={}, IV长度={}, 标签长度={}", 
                   enabled, algorithm, keySize, gcmIvLength, gcmTagLength);
    }

    /**
     * 初始化默认密钥
     */
    private void initializeDefaultKeys() throws Exception {
        // 生成主密钥
        generateNewKey(masterKeyAlias);
        
        // 生成默认密钥
        generateNewKey(defaultKeyAlias);
        
        logger.info("默认加密密钥初始化完成");
    }

    /**
     * 初始化敏感字段配置
     */
    private void initializeSensitiveFields() {
        // 添加常见的敏感字段
        sensitiveFields.add("password");
        sensitiveFields.add("token");
        sensitiveFields.add("secret");
        sensitiveFields.add("key");
        sensitiveFields.add("ssn");
        sensitiveFields.add("creditCard");
        sensitiveFields.add("phone");
        sensitiveFields.add("email");
        sensitiveFields.add("address");
        
        logger.info("敏感字段配置初始化完成: {} 个字段", sensitiveFields.size());
    }

    /**
     * 初始化字段加密规则
     */
    private void initializeFieldEncryptionRules() {
        // 为不同字段设置不同的加密规则
        fieldEncryptionRules.put("password", new EncryptionRule(defaultKeyAlias, "AES/GCM/NoPadding", true));
        fieldEncryptionRules.put("token", new EncryptionRule(defaultKeyAlias, "AES/GCM/NoPadding", true));
        fieldEncryptionRules.put("secret", new EncryptionRule(masterKeyAlias, "AES/GCM/NoPadding", true));
        fieldEncryptionRules.put("key", new EncryptionRule(masterKeyAlias, "AES/GCM/NoPadding", true));
        
        logger.info("字段加密规则初始化完成: {} 个规则", fieldEncryptionRules.size());
    }

    /**
     * 获取加密密钥
     * 
     * @param keyAlias 密钥别名
     * @return 加密密钥
     */
    private SecretKey getEncryptionKey(String keyAlias) {
        return encryptionKeys.get(keyAlias);
    }

    /**
     * 生成随机IV
     * 
     * @return 随机IV
     */
    private byte[] generateRandomIV() {
        byte[] iv = new byte[gcmIvLength];
        new SecureRandom().nextBytes(iv);
        return iv;
    }

    /**
     * 加密数据类
     */
    public static class EncryptedData {
        private final String data;
        private final String iv;
        private final String keyAlias;
        private final String algorithm;
        private final boolean encrypted;

        public EncryptedData(String data, String iv, String keyAlias, String algorithm, boolean encrypted) {
            this.data = data;
            this.iv = iv;
            this.keyAlias = keyAlias;
            this.algorithm = algorithm;
            this.encrypted = encrypted;
        }

        // Getter方法
        public String getData() { return data; }
        public String getIv() { return iv; }
        public String getKeyAlias() { return keyAlias; }
        public String getAlgorithm() { return algorithm; }
        public boolean isEncrypted() { return encrypted; }
    }

    /**
     * 加密规则类
     */
    public static class EncryptionRule {
        private final String keyAlias;
        private final String algorithm;
        private final boolean required;

        public EncryptionRule(String keyAlias, String algorithm, boolean required) {
            this.keyAlias = keyAlias;
            this.algorithm = algorithm;
            this.required = required;
        }

        // Getter方法
        public String getKeyAlias() { return keyAlias; }
        public String getAlgorithm() { return algorithm; }
        public boolean isRequired() { return required; }

        @Override
        public String toString() {
            return String.format("EncryptionRule{keyAlias='%s', algorithm='%s', required=%s}", 
                               keyAlias, algorithm, required);
        }
    }

    /**
     * 加密统计信息类
     */
    public static class EncryptionStatistics {
        private final long totalEncryptions;
        private final long totalDecryptions;
        private final long totalEncryptionTime;
        private final long totalDecryptionTime;
        private final int keyCount;
        private final int sensitiveFieldCount;
        private final boolean encryptionEnabled;
        private final String defaultAlgorithm;
        private final int keySize;
        private final long timestamp;

        public EncryptionStatistics(long totalEncryptions, long totalDecryptions, long totalEncryptionTime,
                                 long totalDecryptionTime, int keyCount, int sensitiveFieldCount,
                                 boolean encryptionEnabled, String defaultAlgorithm, int keySize, long timestamp) {
            this.totalEncryptions = totalEncryptions;
            this.totalDecryptions = totalDecryptions;
            this.totalEncryptionTime = totalEncryptionTime;
            this.totalDecryptionTime = totalDecryptionTime;
            this.keyCount = keyCount;
            this.sensitiveFieldCount = sensitiveFieldCount;
            this.encryptionEnabled = encryptionEnabled;
            this.defaultAlgorithm = defaultAlgorithm;
            this.keySize = keySize;
            this.timestamp = timestamp;
        }

        // Getter方法
        public long getTotalEncryptions() { return totalEncryptions; }
        public long getTotalDecryptions() { return totalDecryptions; }
        public long getTotalEncryptionTime() { return totalEncryptionTime; }
        public long getTotalDecryptionTime() { return totalDecryptionTime; }
        public int getKeyCount() { return keyCount; }
        public int getSensitiveFieldCount() { return sensitiveFieldCount; }
        public boolean isEncryptionEnabled() { return encryptionEnabled; }
        public String getDefaultAlgorithm() { return defaultAlgorithm; }
        public int getKeySize() { return keySize; }
        public long getTimestamp() { return timestamp; }
    }
}
